-module(worker).
-include("definitions.hrl").


-export([start/5, stop/1, get_restored_state_without_cache/2
%%  , get_restored_state_from_ets/2
]).

%WasFault => восстановить состояние, не использовать параметр TestId
-spec start/5 :: (device_id(), test_id(), pid(), boolean(), boolean()) -> ok.
start(DeviceId, TestId, DevicePid, WasFault, CheckWithExpectation) ->
  random:seed(erlang:now()),
  State = case WasFault of
            true ->
              S1 = get_restored_state_without_cache(DeviceId, TestId),
              S2 = S1#stateRecord{fails = S1#stateRecord.fails + 1},
              S2;
            false ->
              get_restored_state_without_cache(DeviceId, TestId)
          end,
  Test = storage:get_test(State#stateRecord.testId),
  Bits = Test#testCase.dataField,

  SkippedIfRunBefore = if
                         State#stateRecord.sends > 0 ->
                           lists:nthtail(State#stateRecord.sends, Bits);
                         true -> Bits
                       end,
  Answer = if
             State#stateRecord.sends > 0 ->
               lists:nthtail(State#stateRecord.sends, Test#testCase.desiredAnswer);
             true -> Test#testCase.desiredAnswer
           end,

  run_test(DevicePid, SkippedIfRunBefore, Answer, State, CheckWithExpectation),
%%   io:format("worker finished correctly { ~p }~n", [self()]),
  ok.


-spec run_test/5 :: (pid(), [<<_:64>>], [<<_:64>>], state(), boolean()) -> ok.
run_test(_, [], _, State, _) ->
  NewState = State#stateRecord{completed = true, answer = State#stateRecord.answer},
  test_executor:update_state(NewState);

run_test(DevicePid, [H | T], [Answer | Answers], State, CheckWithExpectation) ->
  NewState = process_byte(H, Answer, State, DevicePid, CheckWithExpectation),
  run_test(DevicePid, T, Answers, NewState, CheckWithExpectation).

-spec process_byte/5 :: (
    <<_:64>>,
    <<_:64>>,
    state(),
    pid(),
    boolean())
      -> state().

process_byte(InputByte, Expected, State, DevicePid, CheckWithExpectation) ->
  {ok, EnableRandomErrorsForBenchmark} = application:get_env(errors_on),
  case EnableRandomErrorsForBenchmark of
    true ->
      Rand = random:uniform(40),
      case Rand of
        3 -> exit(self(), errorDevice);
        _ -> ok
      end;
    _ -> ok
  end,

  Answer = get_answer(DevicePid, InputByte),

  BitsRun = State#stateRecord.sends + 1,
  NewState = State#stateRecord{answer = State#stateRecord.answer ++ Answer, sends = BitsRun},

  if
    hd(Answer) == Expected -> ok;
    CheckWithExpectation ->
      LastState = NewState#stateRecord{unexpected_answer = true},
      test_executor:update_state(LastState),
      exit(self(), unexpectedAnswer);
    true -> ok
  end,
  test_executor:update_state(NewState),
%%   case ?BENCHMARK of
%%
%%     true ->
%%       receive
%%         {ok, updatedState} -> ok
%%       after ?TRESHOLD
%%         ->
%%         io:format("db dint answer"),
%%         exit(ok)
%%       end;
%%
%%     _ -> ok
%%   end,
  NewState.

-spec get_answer/2 :: (pid(), <<_:64>>) -> [<<_:64>>].
get_answer(DevicePid, X) ->
  DevicePid ! {message, X, self()},
  receive
    {stop, _} ->
      %{stop, From} ->
      %From ! worker_stopped,
      exit(self(), test_cancelled);

    {answer, Ans} ->
      [Ans]
  end.

%% -spec get_restored_state_from_ets/2 :: (device_id(), test_id()) -> state().
%% get_restored_state_from_ets(DeviceId, TestId) ->
%%   Last = storage_supervisor:get_last_for_device(DeviceId),
%%   case Last#stateRecord.id of
%%                   -1 ->
%%                     NextId = storage:get_next_id(),
%%                     State = #stateRecord{id = NextId, deviceId = DeviceId, testId = TestId},
%%                     storage_supervisor:update_state(State, self()),
%%                     State;%инициализировались
%%                   _ ->
%%                     Last
%%                 end.

-spec get_restored_state_without_cache/2 :: (device_id(), test_id()) -> state().
get_restored_state_without_cache(DeviceId, TestId) ->
  Last = storage:get_last_state_for_device(DeviceId),
  ReturnState = case Last#stateRecord.id of
                  -1 ->
                    NextId = storage:get_next_id(),
                    State = #stateRecord{id = NextId, deviceId = DeviceId, testId = TestId},
                    storage_supervisor:update_state(State, self()),
                    State;%инициализировались
                  _ ->
                    Last
                end,
  ReturnState.

-spec stop/1 :: (pid()) -> ok.
stop(Pid) when is_pid(Pid) ->
  Pid ! {stop, self()},
  ok.